﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters.Soap;
using System.IO;
using System.Net.Sockets;

namespace Common
{
	public static class General
	{
		private static string _requestHeader = "POST / HTTP/1.1" + Environment.NewLine + "host: localhost" + Environment.NewLine + "content-length: ";
		private static string _responseHeader = "HTTP/1.1 200 OK" + Environment.NewLine + "content-length: ";

		public static string RequestHeader { get { return _requestHeader; } }
		public static string ResponseHeader { get { return _responseHeader; } }

		public static byte[] BuildRequest(ICommand cmd)
		{
			StringBuilder res = new StringBuilder(RequestHeader);
			res.Append(Encoding.ASCII.GetBytes(SerializeCommand(cmd)).Length.ToString() + Environment.NewLine + Environment.NewLine);
			res.Append(SerializeCommand(cmd));
			return Encoding.ASCII.GetBytes(res.ToString());
		}

		public static byte[] BuildResponse(ICommand cmd)
		{
			StringBuilder res = new StringBuilder(ResponseHeader);
			res.Append(Encoding.ASCII.GetBytes(SerializeCommand(cmd)).Length.ToString() + Environment.NewLine + Environment.NewLine);
			res.Append(SerializeCommand(cmd));
			return Encoding.ASCII.GetBytes(res.ToString());
		}

		private static string SerializeCommand(ICommand cmd)
		{
			SoapFormatter sf = new SoapFormatter();
			MemoryStream ms = new MemoryStream();
			sf.Serialize(ms, cmd);
			byte[] commBytes = ms.GetBuffer();
			string serializedCommand = Encoding.ASCII.GetString(commBytes);
			//serializedCommand = serializedCommand.Substring(0, serializedCommand.Length - 2);//to remove \r\n in the end
			return serializedCommand;
		}

		public static ICommand GetCommand(string msg)
		{
			SoapFormatter sf = new SoapFormatter();
			byte[] tmp = Encoding.UTF8.GetBytes(msg);
			MemoryStream ms = new MemoryStream(tmp);
			return sf.Deserialize(ms) as ICommand;
		}

		public static bool IsResponse(string cmd)
		{
			return (cmd.Substring(0, 4).ToUpper().Trim() == "HTTP");
		}

		public static bool IsRequest(string cmd)
		{
			return !IsResponse(cmd);
		}

		public static ICommand GetCommandFromStream(NetworkStream stm)
		{
			byte[] buffer = new byte[1024];
			string ch, head = "";
			StringBuilder tmp = new StringBuilder();
			int i, length;

			try
			{
				while (stm.DataAvailable)
				{
					i = stm.ReadByte();
					if (i != -1)
						try
						{
							if (head.Length < 4)
								head += Encoding.ASCII.GetString(new byte[] { (byte)i });
							else
								head = head.Substring(1) + Encoding.ASCII.GetString(new byte[] { (byte)i });
						}
						catch (ObjectDisposedException e)
						{
							// The stream is closed
						}
					else
						throw new IOException("No data to read from stream!");

					if (head.ToUpper() == "HTTP" || head.ToUpper() == "POST")
						break;
				}

				switch (head)
				{
					case "HTTP":
						stm.Read(buffer, 0, Encoding.ASCII.GetByteCount(ResponseHeader) - 4);
						break;
					case "POST":
						stm.Read(buffer, 0, Encoding.ASCII.GetByteCount(RequestHeader) - 4);
						break;
					default:
						throw new IOException("Can't read the command!");
				}

				ch = Encoding.ASCII.GetString(new byte[] { (byte)stm.ReadByte() });
				do
				{
					tmp.Append(ch);
					ch = Encoding.ASCII.GetString(new byte[] { (byte)stm.ReadByte() });
				} while (ch != Environment.NewLine.Substring(0, 1));

				if (!int.TryParse(tmp.ToString(), out length))
					throw new IOException("Can't read the command!");

				stm.Read(buffer, 0, Environment.NewLine.Length * 2 - 1);

				tmp = new StringBuilder();
				do
				{
					i = stm.Read(buffer, 0, (buffer.Length < length) ? buffer.Length : length);
					tmp.Append(Encoding.ASCII.GetString(buffer, 0, i));
					length -= i;
				} while (length > 0);
			}
			catch (IOException e)
			{
				// Socket is closed
				return null;
			}
			catch (ObjectDisposedException e)
			{
				// Stream closed or Read error
				return null;
			}

			return GetCommand(tmp.ToString());
		}
	}
}